using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using static LightCAD.MathLib.Constants;
using LightCAD.Three.OpenGL;
using System.Collections.Concurrent;
using LightCAD.MathLib;

namespace LightCAD.Three
{
    public class WebGLShaderStage
    {
        #region scope properties or methods
        //private static int _id = 0;
        private static ConcurrentDictionary<int, int> objIndex = new ConcurrentDictionary<int, int>();
        #endregion

        public int id;
        public string code;
        public int usedTimes;

        public WebGLShaderStage(string code)
        {
            this.id = ThreeThreadContext.NewId(objIndex);
            this.code = code;
            this.usedTimes = 0;
        }
    }
    public class WebGLShaderCache
    {

        #region Properties

        public JsObj<WebGLShaderStage> shaderCache;
        public JsObj<ShaderMaterial, ListEx<WebGLShaderStage>> materialCache;

        #endregion

        #region constructor
        public WebGLShaderCache()
        {
            this.shaderCache = new JsObj<WebGLShaderStage>();
            this.materialCache = new JsObj<ShaderMaterial, ListEx<WebGLShaderStage>>();
        }
        #endregion

        #region methods
        public WebGLShaderCache update(ShaderMaterial material)
        {
            var vertexShader = material.vertexShader;
            var fragmentShader = material.fragmentShader;
            var vertexShaderStage = this._getShaderStage(vertexShader);
            var fragmentShaderStage = this._getShaderStage(fragmentShader);
            var materialShaders = this._getShaderCacheForMaterial(material);
            if (materialShaders.Has(vertexShaderStage) == false)
            {
                materialShaders.Add(vertexShaderStage);
                vertexShaderStage.usedTimes++;
            }
            if (materialShaders.Has(fragmentShaderStage) == false)
            {
                materialShaders.Add(fragmentShaderStage);
                fragmentShaderStage.usedTimes++;
            }
            return this;
        }
        public WebGLShaderCache remove(ShaderMaterial material)
        {
            var materialShaders = this.materialCache.get(material);
            foreach (var shaderStage in materialShaders)
            {
                shaderStage.usedTimes--;
                if (shaderStage.usedTimes == 0) this.shaderCache.delete(shaderStage.code);
            }
            this.materialCache.delete(material);
            return this;
        }
        public int getVertexShaderID(ShaderMaterial material)
        {
            return this._getShaderStage(material.vertexShader).id;
        }
        public int getFragmentShaderID(ShaderMaterial material)
        {
            return this._getShaderStage(material.fragmentShader).id;
        }
        public void dispose()
        {
            this.shaderCache.Clear();
            this.materialCache.Clear();
        }
        public ListEx<WebGLShaderStage> _getShaderCacheForMaterial(ShaderMaterial material)
        {
            var cache = this.materialCache;
            var set = cache.get(material);
            if (set == null)
            {
                set = new ListEx<WebGLShaderStage>();
                cache.set(material, set);
            }
            return set;
        }
        public WebGLShaderStage _getShaderStage(string code)
        {
            var cache = this.shaderCache;
            var stage = cache.get(code);
            if (stage == null)
            {
                stage = new WebGLShaderStage(code);
                cache.set(code, stage);
            }
            return stage;
        }
        #endregion

    }
}
